//package java_cup;

//import java.util.Stack;


using System.Text;

/**
 * This class represents an LALR item. Each LALR item consists of a production,
 * a "dot" at a dotPosition within that production, and a set of lookahead EmitSymbols
 * (terminal). (The first two of these parts are provide by the base class). An
 * item is designed to represent a configuration that the CUPParser may be in. For
 * example, an item of the form:
 * 
 * <pre>
 *    [A ::= B * C d E  , {a,b,c}]
 * </pre>
 * 
 * indicates that the CUPParser is in the middle of parsing the production
 * 
 * <pre>
 *    A ::= B C d E
 * </pre>
 * 
 * that B has already been parsed, and that we will expect to see a lookahead of
 * either a, b, or c once the complete RHS of this production has been found.
 * <p>
 *
 * Items may initially be missing some ItemSet from their lookahead sets. Links
 * are maintained from each item to the set of ItemSet that would need to be
 * updated if EmitSymbols are added to its lookahead set. During "lookahead
 * propagation", we TryAdd EmitSymbols to various lookahead sets and propagate these
 * changes across these dependency links as needed.
 * 
 * @see java_cup.lalr_item_set
 * @see java_cup.LALRState
 * @version last updated: 11/25/95
 * @author Scott Hudson
 */
public class LALRItem : LRItemCore {

    /*-----------------------------------------------------------*/
    /*--- Constructor(s) ----------------------------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Full constructor.
     * 
     * @param production the production for the item.
     * @param dotPosition  the dotPosition of the "dot" within the production.
     * @param lookAheads the set of lookahead EmitSymbols.
     */
    public LALRItem(ProductionDraft production, int dotPosition, VtSet lookAheads)
          /*throws internal_error*/
          : base(production, dotPosition) {
        _lookAheads = lookAheads;
        _propagateItems = new();
        _needPropagation = true;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    ///**
    // * Constructor with default dotPosition (dot at start).
    // * 
    // * @param production the production for the item.
    // * @param lookAheads the set of lookahead EmitSymbols.
    // */
    //public LALRItem(ProductionDraft production, VtSet lookAheads)
    //    /*throws internal_error*/
    //    : this(production, 0, lookAheads) {
    //}

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Constructor with default position and empty lookahead set.
     * 
     * @param production the production for the item.
     */
    public LALRItem(ProductionDraft production)
        /*throws internal_error*/
        : this(production, 0, new VtSet()) {
    }

    /*-----------------------------------------------------------*/
    /*--- (Access to) Instance Variables ------------------------*/
    /*-----------------------------------------------------------*/

    /** The lookahead EmitSymbols of the item. */
    public VtSet _lookAheads;

    /** The lookahead EmitSymbols of the item. */
    public VtSet LookAheads => _lookAheads;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Links to ItemSet that the lookahead needs to be propagated to. */
    //public Stack<lalr_item> _propagateItems;
    public List<LALRItem> _propagateItems;

    /** Links to ItemSet that the lookahead needs to be propagated to */
    public List<LALRItem> PropagateItems => _propagateItems;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Flag to indicate that this item needs to propagate its lookahead (whether it
     * has changed or not).
     */
    public bool _needPropagation;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Add a new item to the set of ItemSet we propagate to. */
    public void AddPropagate(LALRItem itemee) {
        //_propagateItems.Push(prop_to);
        _propagateItems.Add(itemee);
        _needPropagation = true;
    }

    /*-----------------------------------------------------------*/
    /*--- General Methods ---------------------------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Propagate incoming lookaheads through this item to others need to be changed.
     * 
     * @params incoming EmitSymbols to potentially be added to lookahead of this item.
     */
    public void PropagateLookAheads(VtSet incoming) /*throws internal_error*/ {
        bool change = false;

        /* if we don't need to propagate, then bail @out now */
        if (!_needPropagation && (incoming == null || incoming.empty()))
            return;

        /* if we have null incoming, treat as an empty set */
        if (incoming != null) {
            /* TryAdd the incoming to the lookahead of this item */
            change = this.LookAheads.AddRange(incoming);
        }

        /* if we changed or need it anyway, propagate across our links */
        if (change || _needPropagation) {
            /* don't need to propagate again */
            _needPropagation = false;

            /* propagate our lookahead into each item we are linked to */
            for (int i = 0; i < PropagateItems.Count; i++)
                this.PropagateItems[i].PropagateLookAheads(this.LookAheads);
        }
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Produce the new lalr_item that results from shifting the dot one dotPosition to
     * the _right.
     */
    public LALRItem Shift() /*throws internal_error*/ {

        /* can't Shift if we have dot already at the end */
        if (dotAtEnd)
            throw new InternalError("Attempt to Shift past end of an lalr_item");

        /* create the new item w/ the dot shifted by one */
        var result = new LALRItem(ProductionDraft, DotPosition + 1, new VtSet(LookAheads));

        /* change in our lookahead needs to be propagated to this item */
        AddPropagate(result);

        return result;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    // lookAheads + lookAheads2 in yac
    /**
     * Calculate lookahead representing EmitSymbols that could appear after the SymbolDraft
     * that the dot is currently in front of. Note: this routine must not be invoked
     * before first sets and nullable has been calculated for AllVns non terminals.
     */
    public VtSet calcLookahead(VtSet lookaheadAfter) /*throws internal_error*/ {
        /* sanity check */
        if (dotAtEnd)
            throw new InternalError("Attempt to calculate a lookahead set with a completed item");

        /* start with an empty result */
        var result = new VtSet();

        /* consider AllVns nullable EmitSymbols after the one to the _right of the dot */
        for (int pos = DotPosition + 1; pos < ProductionDraft.rhsLength; pos++) {
            ProductionPart part = ProductionDraft.rhs(pos);

            /* consider what Kind of production part it is -- skip actions */
            if (part is SymbolPart symbolPart) {
                SymbolDraft symbol = symbolPart.TheSymbolDraft;

                /* if its a terminal TryAdd it in and we are done */
                if (!symbol.IsVn) {
                    var Vt_ = (Vt)symbol;
                    result.Add(Vt_);
                    return result;
                }
                else {
                    /* otherwise TryAdd in first set of the non terminal */
                    var Vn_ = (Vn)symbol;
                    result.AddRange(Vn_.firstSet);

                    /* if its nullable we continue adding, if not, we are done */
                    if (!Vn_.nullable) { return result; }
                }
            }
        }

        /*
         * if we get here everything past the dot was nullable
         * we TryAdd in the lookahead
         * for after the production and we are done
         */
        result.AddRange(lookaheadAfter);
        return result;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Determine if everything from the SymbolDraft one beyond the dot AllVns the way to the
     * end of the _right hand side is nullable. This would indicate that the
     * lookahead of this item must be included in the lookaheads of AllVns ItemSet
     * produced as a closure of this item. Note: this routine should not be invoked
     * until after first sets and nullable have been calculated for AllVns non
     * terminals.
     */
    public bool LookaheadVisible() /*throws internal_error*/ {
        /*
         * if the dot is at the end, we have a problem, but the cleanest thing to do is
         * just return true.
         */
        if (this.dotAtEnd) { return true; }

        /* walk down the rhs and bail if we get a non-nullable SymbolDraft */
        for (int pos = DotPosition + 1; pos < ProductionDraft.rhsLength; pos++) {
            ProductionPart part = ProductionDraft.rhs(pos);

            /* skip actions */
            if (!part.IsAction) {
                SymbolDraft symbol = ((SymbolPart)part).TheSymbolDraft;

                /* if its a terminal we fail */
                if (!symbol.IsVn)
                    return false;

                /* if its not nullable we fail */
                if (!((Vn)symbol).nullable)
                    return false;
            }
        }

        /* if we get here its AllVns nullable */
        return true;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Equality comparison -- here we only require the cores to be equal since we
     * need to do sets of ItemSet based only on core equality (ignoring lookahead
     * sets).
     */
    public bool equals(LALRItem other) {
        if (other == null)
            return false;
        return base.Equals(other);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Generic equality comparison. */
    /*@Override*/
    public override bool Equals(Object? other) {
        if (!(other is LALRItem))
            return false;
        else
            return equals((LALRItem)other);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Return a hash code -- here we only hash the core since we only test core
     * matching in LALR ItemSet.
     */
    /*@Override*/
    public override int GetHashCode() {
        return base.GetHashCode();
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Convert to string. */
    /*@Override*/
    public override string ToString() {
        var res = new StringBuilder();

        // additional output for debugging:
        // result += "(" + obj_hash() + ")";
        res.Append("[");
        res.Append(base.ToString());
        res.Append(", ");
        if (LookAheads != null) {
            res.Append("{");
            for (int t = 0; t < Vt.Count; t++)
                if (LookAheads.contains(t))
                    res.Append(Vt.Find(t).Name + " ");
            res.Append("}");
        }
        else
            res.Append("NULL LOOKAHEAD!!");
        res.Append("]");

        // additional output for debugging:
        // result += " -> ";
        // for (int i = 0; i<propagate_items().Count; i++)
        // result+=((lalr_item)(propagate_items().ElementAt(i))).obj_hash()+" ";
        //
        // if (_needPropagation) result += " NP";

        return res.ToString();
    }
    /*-----------------------------------------------------------*/
}
